﻿using System;
using UnityEngine;

namespace Net
{
    [Serializable]
    public struct Vector2 : IEquatable<Vector2>
    {
        public float x;
        public float y;

        public Vector2(float x, float y)
        {
            this.x = x;
            this.y = y;
        }

        public float this[int index]
        {
            get
            {
                if (index == 0) return x;
                if (index == 1) return y;
                throw new IndexOutOfRangeException("Invalid Vector2 index!");
            }
            set
            {
                if (index == 0) x = value;
                else if (index == 1) y = value;
                else throw new IndexOutOfRangeException("Invalid Vector2 index!");
            }
        }

        public void Set(float newX, float newY)
        {
            x = newX;
            y = newY;
        }

        public static Vector2 Lerp(Vector2 a, Vector2 b, float t)
        {
            t = Mathf.Clamp01(t);
            return new Vector2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t);
        }

        public static Vector2 LerpUnclamped(Vector2 a, Vector2 b, float t)
        {
            return new Vector2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t);
        }

        public static Vector2 MoveTowards(Vector2 current, Vector2 target, float maxDistanceDelta)
        {
            var a = target - current;
            var magnitude = a.magnitude;
            if (magnitude <= maxDistanceDelta || magnitude == 0f)
                return target;
            return current + a / magnitude * maxDistanceDelta;
        }

        public static bool MoveTowards(ref Vector2 current, Vector2 target, float maxDistanceDelta)
        {
            var a = target - current;
            var magnitude = a.magnitude;
            if (magnitude <= maxDistanceDelta || magnitude == 0f)
            {
                current = target;
                return true;
            }
            current += a / magnitude * maxDistanceDelta;
            return false;
        }

        public static Vector2 Scale(Vector2 a, Vector2 b)
        {
            return new Vector2(a.x * b.x, a.y * b.y);
        }

        public void Scale(Vector2 scale)
        {
            x *= scale.x;
            y *= scale.y;
        }

        public void Normalize()
        {
            float magnitude = this.magnitude;
            if (magnitude > 1E-05f)
                this /= magnitude;
            else
                this = zero;
        }

        [Newtonsoft_X.Json.JsonIgnore]
        public Vector2 normalized
        {
            get
            {
                Vector2 result = new Vector2(x, y);
                result.Normalize();
                return result;
            }
        }

        public override string ToString()
        {
            return $"({x:F1}, {y:F1})";
        }

        public string ToString(string format)
        {
            return $"({x.ToString(format)}, {y.ToString(format)})";
        }

        public override int GetHashCode()
        {
            return x.GetHashCode() ^ y.GetHashCode() << 2;
        }

        public override bool Equals(object other)
        {
            return other is Vector2 other2 && Equals(other2);
        }

        public bool Equals(Vector2 other)
        {
            return this == other;
        }

        public static Vector2 Reflect(Vector2 inDirection, Vector2 inNormal)
        {
            return -2f * Dot(inNormal, inDirection) * inNormal + inDirection;
        }

        public static Vector2 Perpendicular(Vector2 inDirection)
        {
            return new Vector2(-inDirection.y, inDirection.x);
        }

        public static float Dot(Vector2 lhs, Vector2 rhs)
        {
            return lhs.x * rhs.x + lhs.y * rhs.y;
        }

        public float magnitude => Mathf.Sqrt(x * x + y * y);
        public float sqrMagnitude => x * x + y * y;
        public readonly float Sum => x + y;

        public static float Angle(Vector2 from, Vector2 to)
        {
            float num = Mathf.Sqrt(from.sqrMagnitude * to.sqrMagnitude);
            if (num < 1E-15f)
                return 0f;
            float f = Mathf.Clamp(Dot(from, to) / num, -1f, 1f);
            return Mathf.Acos(f) * 57.29578f;
        }

        public static float SignedAngle(Vector2 from, Vector2 to)
        {
            float num = Angle(from, to);
            float num2 = Mathf.Sign(from.x * to.y - from.y * to.x);
            return num * num2;
        }

        public static float Distance(Vector2 a, Vector2 b)
        {
            return (a - b).magnitude;
        }

        public static Vector2 ClampMagnitude(Vector2 vector, float maxLength)
        {
            return vector.sqrMagnitude > maxLength * maxLength ? vector.normalized * maxLength : vector;
        }

        public static float SqrMagnitude(Vector2 a)
        {
            return a.x * a.x + a.y * a.y;
        }

        public float SqrMagnitude()
        {
            return x * x + y * y;
        }

        public static Vector2 Min(Vector2 lhs, Vector2 rhs)
        {
            return new Vector2(Mathf.Min(lhs.x, rhs.x), Mathf.Min(lhs.y, rhs.y));
        }

        public static Vector2 Max(Vector2 lhs, Vector2 rhs)
        {
            return new Vector2(Mathf.Max(lhs.x, rhs.x), Mathf.Max(lhs.y, rhs.y));
        }

        public static Vector2 SmoothDamp(Vector2 current, Vector2 target, ref Vector2 currentVelocity, float smoothTime, float maxSpeed)
        {
            return SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed, Time.deltaTime);
        }

        public static Vector2 SmoothDamp(Vector2 current, Vector2 target, ref Vector2 currentVelocity, float smoothTime)
        {
            return SmoothDamp(current, target, ref currentVelocity, smoothTime, float.PositiveInfinity, Time.deltaTime);
        }

        public static Vector2 SmoothDamp(Vector2 current, Vector2 target, ref Vector2 currentVelocity, float smoothTime, float maxSpeed, float deltaTime)
        {
            smoothTime = Mathf.Max(0.0001f, smoothTime);
            float num = 2f / smoothTime;
            float num2 = num * deltaTime;
            float d = 1f / (1f + num2 + 0.48f * num2 * num2 + 0.235f * num2 * num2 * num2);
            Vector2 vector = current - target;
            Vector2 vector2 = target;
            float maxLength = maxSpeed * smoothTime;
            vector = ClampMagnitude(vector, maxLength);
            target = current - vector;
            Vector2 vector3 = (currentVelocity + num * vector) * deltaTime;
            currentVelocity = (currentVelocity - num * vector3) * d;
            Vector2 vector4 = target + (vector + vector3) * d;
            if (Dot(vector2 - current, vector4 - vector2) > 0f)
            {
                vector4 = vector2;
                currentVelocity = (vector4 - vector2) / deltaTime;
            }
            return vector4;
        }

        public static Vector2 operator +(Vector2 a, Vector2 b) => new Vector2(a.x + b.x, a.y + b.y);
        public static Vector2 operator -(Vector2 a, Vector2 b) => new Vector2(a.x - b.x, a.y - b.y);
        public static Vector2 operator *(Vector2 a, Vector2 b) => new Vector2(a.x * b.x, a.y * b.y);
        public static Vector2 operator /(Vector2 a, Vector2 b) => new Vector2(a.x / b.x, a.y / b.y);
        public static Vector2 operator -(Vector2 a) => new Vector2(-a.x, -a.y);
        public static Vector2 operator *(Vector2 a, float d) => new Vector2(a.x * d, a.y * d);
        public static Vector2 operator *(float d, Vector2 a) => new Vector2(a.x * d, a.y * d);
        public static Vector2 operator /(Vector2 a, float d) => new Vector2(a.x / d, a.y / d);
        public static bool operator ==(Vector2 lhs, Vector2 rhs) => (lhs - rhs).sqrMagnitude < 9.9999994E-11f;
        public static bool operator !=(Vector2 lhs, Vector2 rhs) => !(lhs == rhs);
        public static implicit operator Vector2(Vector3 v) => new Vector2(v.x, v.y);
        public static implicit operator Vector3(Vector2 v) => new Vector3(v.x, v.y, 0f);

        public static Vector2 zero => zeroVector;
        public static Vector2 one => oneVector;
        public static Vector2 up => upVector;
        public static Vector2 down => downVector;
        public static Vector2 left => leftVector;
        public static Vector2 right => rightVector;
        public static Vector2 positiveInfinity => positiveInfinityVector;
        public static Vector2 negativeInfinity => negativeInfinityVector;

        private static readonly Vector2 zeroVector = new Vector2(0f, 0f);
        private static readonly Vector2 oneVector = new Vector2(1f, 1f);
        private static readonly Vector2 upVector = new Vector2(0f, 1f);
        private static readonly Vector2 downVector = new Vector2(0f, -1f);
        private static readonly Vector2 leftVector = new Vector2(-1f, 0f);
        private static readonly Vector2 rightVector = new Vector2(1f, 0f);
        private static readonly Vector2 positiveInfinityVector = new Vector2(float.PositiveInfinity, float.PositiveInfinity);
        private static readonly Vector2 negativeInfinityVector = new Vector2(float.NegativeInfinity, float.NegativeInfinity);

        public const float kEpsilon = 1E-05f;
        public const float kEpsilonNormalSqrt = 1E-15f;

        public float Length() => (float)Math.Sqrt(x * (double)x + y * (double)y);
        public float LengthSquared() => (float)(x * (double)x + y * (double)y);

        public static void Distance(ref Vector2 value1, ref Vector2 value2, out float result)
        {
            float num1 = value1.x - value2.x;
            float num2 = value1.y - value2.y;
            result = (float)Math.Sqrt(num1 * num1 + num2 * num2);
        }

        public static float DistanceSquared(Vector2 value1, Vector2 value2)
        {
            float num1 = value1.x - value2.x;
            float num2 = value1.y - value2.y;
            return num1 * num1 + num2 * num2;
        }

        public static void DistanceSquared(ref Vector2 value1, ref Vector2 value2, out float result)
        {
            float num1 = value1.x - value2.x;
            float num2 = value1.y - value2.y;
            result = num1 * num1 + num2 * num2;
        }

        public static Vector2 Normalize(Vector2 value)
        {
            float num1 = value.x * value.x + value.y * value.y;
            if (num1 < 9.99999974737875E-06)
                return value;
            float num2 = 1f / (float)Math.Sqrt(num1);
            return new Vector2(value.x * num2, value.y * num2);
        }

        public static void Normalize(ref Vector2 value, out Vector2 result)
        {
            float num1 = value.x * value.x + value.y * value.y;
            if (num1 < 9.99999974737875E-06)
            {
                result = value;
            }
            else
            {
                float num2 = 1f / (float)Math.Sqrt(num1);
                result = new Vector2(value.x * num2, value.y * num2);
            }
        }

        public static void Reflect(ref Vector2 vector, ref Vector2 normal, out Vector2 result)
        {
            float num = vector.x * normal.x + vector.y * normal.y;
            result = new Vector2(vector.x - 2f * num * normal.x, vector.y - 2f * num * normal.y);
        }

        public static void Min(ref Vector2 value1, ref Vector2 value2, out Vector2 result)
        {
            result = new Vector2(
                value1.x < value2.x ? value1.x : value2.x,
                value1.y < value2.y ? value1.y : value2.y);
        }

        public static void Max(ref Vector2 value1, ref Vector2 value2, out Vector2 result)
        {
            result = new Vector2(
                value1.x > value2.x ? value1.x : value2.x,
                value1.y > value2.y ? value1.y : value2.y);
        }

        public static Vector2 Clamp(Vector2 value1, Vector2 min, Vector2 max)
        {
            return new Vector2(
                Mathf.Clamp(value1.x, min.x, max.x),
                Mathf.Clamp(value1.y, min.y, max.y));
        }

        public static void Clamp(ref Vector2 value1, ref Vector2 min, ref Vector2 max, out Vector2 result)
        {
            result = new Vector2(
                Mathf.Clamp(value1.x, min.x, max.x),
                Mathf.Clamp(value1.y, min.y, max.y));
        }

        public static void Lerp(ref Vector2 value1, ref Vector2 value2, float amount, out Vector2 result)
        {
            result = new Vector2(
                value1.x + (value2.x - value1.x) * amount,
                value1.y + (value2.y - value1.y) * amount);
        }

        public static Vector2 SmoothStep(Vector2 value1, Vector2 value2, float amount)
        {
            amount = Mathf.Clamp01(amount);
            amount = amount * amount * (3f - 2f * amount);
            return new Vector2(
                value1.x + (value2.x - value1.x) * amount,
                value1.y + (value2.y - value1.y) * amount);
        }

        public static void SmoothStep(ref Vector2 value1, ref Vector2 value2, float amount, out Vector2 result)
        {
            amount = Mathf.Clamp01(amount);
            amount = amount * amount * (3f - 2f * amount);
            result = new Vector2(
                value1.x + (value2.x - value1.x) * amount,
                value1.y + (value2.y - value1.y) * amount);
        }

        public static Vector2 Negate(Vector2 value) => new Vector2(-value.x, -value.y);
        public static void Negate(ref Vector2 value, out Vector2 result) => result = new Vector2(-value.x, -value.y);
        public static void Dot(ref Vector2 value1, ref Vector2 value2, out float result) => result = value1.x * value2.x + value1.y * value2.y;
        public static void Angle(ref Vector2 from, ref Vector2 to, out float result)
        {
            from.Normalize();
            to.Normalize();
            Dot(ref from, ref to, out float result1);
            result = Mathf.Acos(Mathf.Clamp(result1, -1f, 1f)) * 57.29578f;
        }

        public static Vector2 Add(Vector2 value1, Vector2 value2) => new Vector2(value1.x + value2.x, value1.y + value2.y);
        public static void Add(ref Vector2 value1, ref Vector2 value2, out Vector2 result) => result = new Vector2(value1.x + value2.x, value1.y + value2.y);
        public static Vector2 Sub(Vector2 value1, Vector2 value2) => new Vector2(value1.x - value2.x, value1.y - value2.y);
        public static void Sub(ref Vector2 value1, ref Vector2 value2, out Vector2 result) => result = new Vector2(value1.x - value2.x, value1.y - value2.y);
        public static Vector2 Multiply(Vector2 value1, Vector2 value2) => new Vector2(value1.x * value2.x, value1.y * value2.y);
        public static void Multiply(ref Vector2 value1, ref Vector2 value2, out Vector2 result) => result = new Vector2(value1.x * value2.x, value1.y * value2.y);
        public static Vector2 Multiply(Vector2 value1, float scaleFactor) => new Vector2(value1.x * scaleFactor, value1.y * scaleFactor);
        public static void Multiply(ref Vector2 value1, float scaleFactor, out Vector2 result) => result = new Vector2(value1.x * scaleFactor, value1.y * scaleFactor);
        public static Vector2 Divide(Vector2 value1, Vector2 value2) => new Vector2(value1.x / value2.x, value1.y / value2.y);
        public static void Divide(ref Vector2 value1, ref Vector2 value2, out Vector2 result) => result = new Vector2(value1.x / value2.x, value1.y / value2.y);
        public static Vector2 Divide(Vector2 value1, float divider) => new Vector2(value1.x / divider, value1.y / divider);
        public static void Divide(ref Vector2 value1, float divider, out Vector2 result) => result = new Vector2(value1.x / divider, value1.y / divider);

        public static UnityEngine.Vector2 operator +(UnityEngine.Vector2 a, Vector2 b) => new UnityEngine.Vector2(a.x + b.x, a.y + b.y);
        public static UnityEngine.Vector2 operator -(UnityEngine.Vector2 a, Vector2 b) => new UnityEngine.Vector2(a.x - b.x, a.y - b.y);
        public static UnityEngine.Vector2 operator *(UnityEngine.Vector2 a, Vector2 b) => new UnityEngine.Vector2(a.x * b.x, a.y * b.y);
        public static UnityEngine.Vector2 operator /(UnityEngine.Vector2 a, Vector2 b) => new UnityEngine.Vector2(a.x / b.x, a.y / b.y);
        public static Vector2 operator +(Vector2 a, UnityEngine.Vector2 b) => new Vector2(a.x + b.x, a.y + b.y);
        public static Vector2 operator -(Vector2 a, UnityEngine.Vector2 b) => new Vector2(a.x - b.x, a.y - b.y);
        public static Vector2 operator *(Vector2 a, UnityEngine.Vector2 b) => new Vector2(a.x * b.x, a.y * b.y);
        public static Vector2 operator /(Vector2 a, UnityEngine.Vector2 b) => new Vector2(a.x / b.x, a.y / b.y);
        public static bool operator ==(UnityEngine.Vector2 lhs, Vector2 rhs) => (lhs - rhs).sqrMagnitude < 9.9999994E-11f;
        public static bool operator !=(UnityEngine.Vector2 lhs, Vector2 rhs) => !(lhs == rhs);
        public static implicit operator UnityEngine.Vector2(Vector2 v) => new UnityEngine.Vector2(v.x, v.y);
        public static implicit operator Vector2(UnityEngine.Vector2 v) => new Vector2(v.x, v.y);
    }
}